﻿using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
using System.Reflection;
using System.Security.Principal;

namespace maranov.SymlinkManager.ApplicationPrivilege
{
    /// <summary>
    /// Application process privilege analyzer with an automatic restart as Administrator.
    /// </summary>
    /// <seealso cref="http://victorhurdugaci.com/using-uac-with-c-part-1/"/>
    public class ApplicationPrivilegeElevator : maranov.SymlinkManager.ApplicationPrivilege.IApplicationPrivilegeElevator
    {
        /// <summary>
        /// Application process privilege analyzer with an automatic restart as Administrator.
        /// </summary>
        /// <param name="requiredTests">
        /// Tests required to pass the privilege check.
        /// </param>
        public ApplicationPrivilegeElevator(IEnumerable<PrivilegeCheck> requiredTests)
        {
            RequiredTests = requiredTests;
        }

        /// <summary>
        /// Application process privilege analyzer with an automatic restart as Administrator.
        /// </summary>
        /// <param name="requiredTest">
        /// Test required to pass the privilege check.
        /// </param>
        public ApplicationPrivilegeElevator(PrivilegeCheck requiredTest)
        {
            var list = new List<PrivilegeCheck>();
            list.Add(requiredTest);
            RequiredTests = list;
        }

        public IEnumerable<PrivilegeCheck> RequiredTests
        {
            get;
            private set;
        }

        public bool IsElevated()
        {
            WindowsPrincipal pricipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
            return pricipal.IsInRole(WindowsBuiltInRole.Administrator);
        }

        public bool Check()
        {
            bool success = true;
            foreach(var test in RequiredTests)
            {
                success &= test();
                if (!success)
                    break;
            }
            return success;
        }

        public void Elevate(ApplicationTerminator termination)
        {
            if (!IsElevated())
            {
                //TODO: Application parameters?
                ProcessStartInfo processInfo = new ProcessStartInfo();
                processInfo.Verb = "runas";
                processInfo.FileName = Assembly.GetExecutingAssembly().Location;
                try
                {
                    Process.Start(processInfo);
                }
                catch (Win32Exception)
                {
                    //Debug.Assert(false, "Win32Exception" + e.Message + e.ErrorCode);

                    // Do nothing. Probably the user canceled the UAC window
                    throw new ApplicationElevationException(false);
                }
                // New process running, terminate current.
                termination();
            }
            else
            {
                // Can't elevate higher
                throw new ApplicationElevationException(true);
            }
        }

        public void CheckAndElevate(ApplicationTerminator termination)
        {
            if (! Check())
            {
                Elevate(termination);
            }
        }
    }
}
